1. Field of Invention
This invention relates to languages and tools for circuit design, specifically to improving tools to support increased reusability of functions written in a Hardware Description Language.
2. Prior Art
Hardware Description Languages (HDLs) and their associated software tools are being used to successfully translate HDL source code into a form suitable for layout as large integrated circuits or for loading into a Field Programmable Gate Array (FPGA). Standard HDLs such as Verilog and VHDL are available, and newer HDLs such as SystemC, and SystemVerilog are becoming more popular for very complex newer designs.
A circuit is a system of interconnected components designed to perform an electrical function. Most circuits are comprised of active components that each perform a specific function when power is applied. The components send signals to each other via their interconnections. A signal is a controlled time-varying value sent from one point in a circuit to another.
Netlists are usually considered the lowest level form of unrouted circuit representation in a circuit design process. A netlist typically comprises a list of named component or gate instantiations that are connected by wires. The gates are typically basic building blocks that are made available in the technology selected to implement the circuit. Netlists are intended to have behavior equivalent to the functionality expressed in the original source code, although netlists represent real logic devices with inherent performance limitations. Netlists are usually concrete entities with no further abstractions to be resolved in their construction.
The objective of most HDLs is to express circuit functionality in a way that can be translated to a netlist.
All HDLs support the use of some type of high-level functional entity in source code that can be translated into a netlist using appropriate translation tools. Most translation tools can synthesize netlists based on the implied behavior described by these functions. Most HDLs also allow circuit structures to be defined directly.
Within translation tools, HDL source code is subject to tokenizing and parsing steps which create data structures called node trees. These node trees are much more amenable to subsequent processing than source code text. The techniques for writing tokenizers and parsers are well known in compiler art.
Some HDLs are intended for elaboration processing. This processing stage is implemented to work on node trees, and calculates various build parameters prior to starting subsequent processing such as synthesis or structural expansion to gates. Parameters include things like bus widths, or constants to be represented as fixed bus values. Elaboration can also be used to provide abstract control in building circuit structures.
Large circuit designs use function hierarchies to describe designs. A top level function is defined, which calls other functions, and so on down to lower levels, forming a function hierarchy.
At the lowest level in digital systems, only two logic values 0 and 1 are used to represent information at a particular time. Generally, digital logic associates these two logic values with two reliably discernible attributes of real systems in accordance with the laws of physics. In reality, the relation between the attributes and logic levels is purely a matter of definition for a particular technology. In most technologies, each wire is used to carry a small or large voltage to represent a logic 0 or a logic 1 value respectively.
The state of a circuit is defined by the logic levels stored in all its storage elements at a particular time. Storage elements can store logic values until specific types of circuit events direct them to store new values.
Synchronous circuit designs use a clock signal to define when state changes occur in storage devices within a circuit. The clock signal is usually sent from a circuit input to each storage device within the circuit. The idea is that on each active clock event, each storage device samples its inputs derived from state that was set on the previous active clock event. The most common storage devices are usually registers. In particular, a D-type register samples a logic level on its data input at a time defined by a specified active clock edge transition on its clock input. A very short time later, the sampled logic value appears on the register output.
In synchronous designs, the output from one register propagates through various combinational logic devices and back into the inputs of other registers. On each active clock edge, the present state of all the registers are presented as inputs to the combinational logic between them, and the combinational logic outputs determine the states of the registers on the following active clock edge.
Clock signals usually need to be distributed around an integrated circuit. If the relative clock signal delays between the clock input and each clocked device in the circuit are within a limit, then all state changes on that clock signal can be treated as simultaneous. If the skew between delays exceeds this limit, then the output of one clocked device could change quickly enough to be sampled by another clocked device on the same active input clock signal edge event, instead of a previous active event. This timing-sensitive behavior is termed a race condition, and is conventionally avoided by using tools to implement sophisticated clock signal distribution strategies, and by adding gates to delay certain signal paths where they are needed based on timing analysis. An upper skew limit is usually set as a design goal compromise between what is achievable for the selected technology, chosen layout tools, and circuit size.
Combinational logic, as its name suggests, maps each combination of input values to an intended output value. Combinational logic devices propagate input changes to their outputs, usually in a relatively short time period compared with the time interval between active clock edge events.
An advantage of the synchronous design methodology is that it simplifies the timing problems down to one issue: for all allowable register state changes, is the combinational logic between registers able to propagate new states arising from an active clock edge event fast enough to reach the inputs of receiving registers in time to be reliably sampled on the next active clock edge event? Static timing analysis tools are usually able to verify that this is the case, as long as they have accurate timing models for the registers and combinational logic used, and as long as they have accurate wire delay values obtained from layout.
Synchronous designs can form one or more pipelines, where data can be pumped through from register to register on each clock, allowing new data to enter the pipe, and processed data to leave the pipe on each clock. This allows pipelined designs to have a throughput that matches their clock speeds.
Synchronous designs can also form state machines, where the current state of one or more registers, via combinational logic, feed back into their own inputs. For example, counters that count to a new value on each clock are a specialized type of state machine that uses their previous count state to calculate their next count state.
A clock domain is a contiguous region of the circuit whose state changes all occur on the same clock edge event, and whose registers are connected via combinational logic. Some circuit designs use multiple clocks, and each clock can serve one or more clock domains. The relative timings between events in different clock domains are often unknown, and are usually assumed to have unrelated or asynchronous timing. Timing between any pair of registers in a clock domain is fully synchronous. Two or more clock domains on the same clock and the same active clock edge, if subsequently connected together via combinational logic, form one larger contiguous clock domain. Contiguous clock domains can also be formed across function call interfaces.
Clock contexts are statements that control one or more substatements. The clock context names a clock signal, and the active edge on that clock signal for all clocked state changes in the substatements, such as register assignments.
Clock contexts are a great convenience to a designer. Without them, a circuit designer would be forced to define the clock and clock edge on each clocked assignment. This would make source code much more verbose, and harder for the circuit designer to check for errors.
The benefits of clock contexts are that clocked storage components are automatically inferred whenever assignments to storage occur within the context. The inferred component's clock input is automatically connected to the clock signal associated with the clock context. Furthermore, the clocked components are selected by the translation tool so that they change their state when the specified clock edge event occurs. Components are selected by name in instantiations within an output netlist.
Conventional HDLs that allow clock contexts only allow them to be defined with associated absolute edges. This limitation reflects the range of active edges supported in the digital storage devices themselves. An absolute edge is usually only specifiable as one of the three useful types possible on a single clock signal: either a rising edge, or a falling edge, or both edges. A rising edge is conventionally defined as a change in a signal level from a logic 0 to 1, and a falling edge is a change from a logic 1 to 0. Henceforth, unless explicitly indicated otherwise, all specified clock edges are assumed to be active edges, as no state changes occur on other edges.
The problem with most HDLs is that they only allow the definition of fixed and absolute clock edges in clock contexts. This limitation reflects the range of active edges supported in the digital storage devices themselves. An absolute edge is usually only specifiable as one of the three useful combinations possible on a single clock signal: either a rising edge, or a falling edge, or both edges.
For example, the popular Verilog HDL allows a rising edge clock domain to be defined within a module entity, as in the following code fragment:
always @(posedge clk) // state changes occur on the rising edge of ‘clk’
begin // clock context start                // all ‘<=’ assignments to ‘reg’ types now infer rising-edge triggered        // D-registers with their clock input connected to the ‘clk’ signal.        my_reg1<=a; // ‘<=’ assigns ‘a’ to ‘my_reg1’ on rising edge of ‘clk’        my_reg2<=my_reg1; // output of ‘my_reg1’ is input to ‘my_reg2’        
end // clock context end
Note that ‘//’ and any following text on the same line are comments that are ignored in Verilog language processing. The reserved word “posedge” is a Verilog abbreviation for a “positive edge” or rising edge.
The “always” clock context in the above Verilog example is a single statement that controls the interpretation of other statements (separated by ‘;’) within its controlled statement block. The statement block is delimited by the begin-end word pair. The clock context definition in the above example causes a translation software tool to connect both the registers “my_reg1” and “my_reg2” to the “clk” input. For simplicity, the declaration of these registers is not shown. The “posedge” reserved word causes registers to be instantiated by the “<=” assignments in both cases. The register clock inputs are connected to the “clk” signal, and the register types are chosen as rising-edge to comply with the rising edge specification.
One of the advantage of using an HDL that allows the use of clock contexts is that the designer doesn't have to specify a clock signal and clock edge sensitivity for each register assignment controlled by the context. This makes the HDL source code simpler and easier to understand, allowing the use of simple ‘<=’ assignments to registers in the above example.
The equivalent of functions in Verilog are “module” entities. The above “always” statement is used within a module. In Verilog, modules call other modules by instantiating them.
Unfortunately, Verilog can only accept clock context edge specifications using absolute clock edge terms such as “posedge” or “negedge” (a “negative edge”, or falling clock edge) reserved words.
If Verilog source code uses all “posedge” clock edges on a particular clock signal, then modifying the source code to use “negedge” (or vice versa) requires editing the edge type on every clock context in the design.
Clock contexts are very common in source code. If changing the active clock edge is performed manually, then there is no guarantee that the designer will remember to change all source code relating to edge specifications. Errors can lead to a circuit designer having to spend time fixing circuits with functional and timing problems.
While it may be possible to perform automatic text substitutions in source code in order to change one edge type to another, there are difficulties in performing this task externally to the translation tools. Major difficulties are:                (a) The same clock signal may be named differently across the functions or Verilog modules that form a hierarchy.        (b) Some designs use multiple clock signals that need to be considered separately.        (c) HDL source code preprocessing may complicate automatic discovery.        
Therefore it may be difficult to identify the clock contexts with edges that need to be changed.
If designers use a mixture of rising or falling edge clock designs, and if they also need to have reusable libraries of commonly used Verilog modules, then they may have to have a rising and falling clock edge version of each clocked Verilog module type. If rising edge and falling edge versions of a Verilog module are both needed, then the versions must be named differently in source code. Both versions have the same input and output connections, so if a Verilog module with the wrong clock edge is instantiated by another Verilog module, everything may appear compatible, and it may even seem to simulate correctly. However, interface timing between the two modules can be badly effected at higher clock speeds.
An inverter gate can be used to invert a clock signal. It effectively swaps the edge timing behavior of rising and falling edge clock contexts. In principle, clock inverters can be inserted into a clock signal path to convert a rising edge Verilog module to a falling edge Verilog module, or vice versa. However, inverters in the clock signal path severely degrades the effectiveness of clock signal distribution in high performance circuit designs.
Other well known HDLs such as VHDL, SystemC, and SystemVerilog have the same problems as Verilog in regard to limitations associated with absolute clock edges.
Accordingly, a method is needed to allow a function to be used as part of a function hierarchy, no matter what active clock edge is used in the hierarchy. The method should allow a translation tool to implement the design by selecting register devices with the correct edge sensitivities for each distributed clock signal.